package fatworm.util;

import java.util.*;

@SuppressWarnings("serial")
class AssertionFailureError extends Error
{
    AssertionFailureError()
    {
        super();
    }
    AssertionFailureError(String message)
    {
        super(message);
    }
}

/**
 * Provides miscellaneous library routines.
 */
public final class Lib
{
    /**
     * Prevent instantiation.
     */
    private Lib()
    {
    }

    private static Random random = null;

    /**
     * Seed the random number generater. May only be called once.
     * 
     * @param randomSeed
     *            the seed for the random number generator.
     */
    public static void seedRandom(long randomSeed)
    {
        assertTrue(random == null);
        random = new Random(randomSeed);
    }
    /**
     * Return a random integer between 0 and <i>range - 1</i>. Must not be
     * called before <tt>seedRandom()</tt> seeds the random number generator.
     * 
     * @param range
     *            a positive value specifying the number of possible return
     *            values.
     * @return a random integer in the specified range.
     */
    public static int random(int range)
    {
        assertTrue(range > 0);
        return random.nextInt(range);
    }
    /**
     * Return a random double between 0.0 (inclusive) and 1.0 (exclusive).
     * 
     * @return a random double between 0.0 and 1.0.
     */
    public static double random()
    {
        return random.nextDouble();
    }
    /**
     * Asserts that <i>expression</i> is <tt>true</tt>. If not, then Nachos
     * exits with an error message.
     * 
     * @param expression
     *            the expression to assert.
     */
    public static void assertTrue(boolean expression)
    {
        if (!expression)
            throw new AssertionFailureError();
    }
    /**
     * Asserts that <i>expression</i> is <tt>true</tt>. If not, then Nachos
     * exits with the specified error message.
     * 
     * @param expression
     *            the expression to assert.
     * @param message
     *            the error message.
     */
    public static void assertTrue(boolean expression, String message)
    {
        if (!expression)
            throw new AssertionFailureError(message);
    }
    /**
     * Asserts that this call is never made. Same as <tt>assertTrue(false)</tt>.
     */
    public static void assertNotReached()
    {
        assertTrue(false);
    }
    /**
     * Asserts that this call is never made, with the specified error messsage.
     * Same as <tt>assertTrue(false, message)</tt>.
     * 
     * @param message
     *            the error message.
     */
    public static void assertNotReached(String message)
    {
        assertTrue(false, message);
    }
    /**
     * Mask out and shift a bit substring.
     * 
     * @param bits
     *            the bit string.
     * @param lowest
     *            the first bit of the substring within the string.
     * @param size
     *            the number of bits in the substring.
     * @return the substring.
     */
    public static int extract(int bits, int lowest, int size)
    {
        if (size == 32)
            return (bits >> lowest);
        else
            return ((bits >> lowest) & ((1 << size) - 1));
    }
    /**
     * Mask out and shift a bit substring.
     * 
     * @param bits
     *            the bit string.
     * @param lowest
     *            the first bit of the substring within the string.
     * @param size
     *            the number of bits in the substring.
     * @return the substring.
     */
    public static long extract(long bits, int lowest, int size)
    {
        if (size == 64)
            return (bits >> lowest);
        else
            return ((bits >> lowest) & ((1L << size) - 1));
    }
    /**
     * Mask out and shift a bit substring; then sign extend the substring.
     * 
     * @param bits
     *            the bit string.
     * @param lowest
     *            the first bit of the substring within the string.
     * @param size
     *            the number of bits in the substring.
     * @return the substring, sign-extended.
     */
    public static int extend(int bits, int lowest, int size)
    {
        int extra = 32 - (lowest + size);
        return ((extract(bits, lowest, size) << extra) >> extra);
    }
    /**
     * Test if a bit is set in a bit string.
     * 
     * @param flag
     *            the flag to test.
     * @param bits
     *            the bit string.
     * @return <tt>true</tt> if <tt>(bits & flag)</tt> is non-zero.
     */
    public static boolean test(long flag, long bits)
    {
        return ((bits & flag) != 0);
    }
    /**
     * Creates a padded upper-case string representation of the integer argument
     * in base 16.
     * 
     * @param i
     *            an integer.
     * @return a padded upper-case string representation in base 16.
     */
    public static String toHexString(int i)
    {
        return toHexString(i, 8);
    }
    /**
     * Creates a padded upper-case string representation of the integer argument
     * in base 16, padding to at most the specified number of digits.
     * 
     * @param i
     *            an integer.
     * @param pad
     *            the minimum number of hex digits to pad to.
     * @return a padded upper-case string representation in base 16.
     */
    public static String toHexString(int i, int pad)
    {
        String result = Integer.toHexString(i).toUpperCase();
        while (result.length() < pad)
            result = "0" + result;
        return result;
    }
    /**
     * Divide two non-negative integers, round the quotient up to the nearest
     * integer, and return it.
     * 
     * @param a
     *            the numerator.
     * @param b
     *            the denominator.
     * @return <tt>ceiling(a / b)</tt>.
     */
    public static int divRoundUp(int a, int b)
    {
        assertTrue(a >= 0 && b > 0);
        return ((a + (b - 1)) / b);
    }
    /**
     * Calculate float modular float
     * 
     */
    public static float floatMod(float a, float b)
    {
        float x, y;
        x = Math.abs(a);
        y = Math.abs(b);
        while (x >= y)
            x -= y;
        if (a >= 0)
            return x;
        else
            return -x;
    }
    public static void debug(String s)
    {
        System.out.println(s);
    }
    /**
     * Convert an int into its big-endian byte string representation.
     * 
     * @param array
     *            the array in which to store the byte string.
     * @param offset
     *            the offset in the array where the string will start.
     * @param value
     *            the value to convert.
     */
    public static void bytesFromInt(byte[] array, int offset, int value)
    {
        array[offset + 3] = (byte) ((value >> 0) & 0xFF);
        array[offset + 2] = (byte) ((value >> 8) & 0xFF);
        array[offset + 1] = (byte) ((value >> 16) & 0xFF);
        array[offset + 0] = (byte) ((value >> 24) & 0xFF);
    }
    /**
     * Convert to an int from its big-endian byte string representation.
     * 
     * @param array
     *            the array containing the byte string.
     * @param offset
     *            the offset of the byte string in the array.
     * @return the corresponding int value.
     */
    public static int bytesToInt(byte[] array, int offset)
    {
        return (int) ((((int) array[offset + 3] & 0xFF) << 0)
                | (((int) array[offset + 2] & 0xFF) << 8)
                | (((int) array[offset + 1] & 0xFF) << 16) | (((int) array[offset + 0] & 0xFF) << 24));
    }
}
